package com.xique.park.service.service.impl;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.xique.common.core.domain.R;
import com.xique.common.core.enums.PayStatus;
import com.xique.common.core.enums.PayType;
import com.xique.common.core.utils.DateUtils;
import com.xique.common.core.utils.SecurityUtils;
import com.xique.common.core.utils.StringUtils;
import com.xique.common.core.web.domain.PageResult;
import com.xique.common.datascope.annotation.NeedSetDictLabel;
import com.xique.common.datascope.annotation.NeedSetFieldValue;
import com.xique.park.bean.domain.ParkStatisticsIncome;
import com.xique.park.bean.domain.PayOrder;
import com.xique.park.bean.domain.RefundOrder;
import com.xique.park.bean.dto.PayOrderDto;
import com.xique.park.bean.request.web.payorder.PayOrderRequest;
import com.xique.park.bean.vo.web.payOrder.PayOrderVo;
import com.xique.park.enums.PayChannelEnum;
import com.xique.park.mapper.PayOrderMapper;
import com.xique.park.service.service.IPayOrderService;
import com.xique.park.service.service.IRefundOrderService;
import com.xique.system.api.RemoteSystemProjectService;
import com.xique.system.api.RemoteSystemUserService;
import com.xique.system.api.dto.OperateDTO;
import com.xique.system.api.dto.SystemProjectDTO;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;

import java.math.BigDecimal;
import java.util.*;
import java.util.stream.Collectors;

/**
 * 支付记录Service业务层处理
 *
 * @author ww
 * @date 2021-12-22
 */
@Slf4j
@RequiredArgsConstructor
@Service
public class PayOrderServiceImpl extends ServiceImpl<PayOrderMapper, PayOrder> implements IPayOrderService {

    private final PayOrderMapper payOrderMapper;

    private final IRefundOrderService refundOrderService;

    private final RemoteSystemUserService remoteSystemUserService;

    private final RemoteSystemProjectService remoteSystemProjectService;

    /**
     * 查询支付记录列表
     *
     * @param tPayOrder 支付记录
     * @return 支付记录
     */
    @Override
    public List<PayOrder> selectTPayOrderList(PayOrder tPayOrder) {
        LambdaQueryWrapper<PayOrder> wrapper = Wrappers.lambdaQuery();
        if (null != SecurityUtils.getParkId()) {
            wrapper.eq(PayOrder::getParkId, SecurityUtils.getParkId());
        }
        if (StringUtils.isNotEmpty(tPayOrder.getPlate())) {
            wrapper.like(PayOrder::getPlate, tPayOrder.getPlate());
        }
        if (StringUtils.isNotEmpty(tPayOrder.getTransType())) {
            wrapper.like(PayOrder::getTransType, tPayOrder.getTransType());
        }
        if (StringUtils.isNotEmpty(tPayOrder.getParkingSerial())) {
            wrapper.eq(PayOrder::getParkingSerial, tPayOrder.getParkingSerial());
        }
        return baseMapper.selectList(wrapper);
    }

    /**
     * 新增支付记录
     *
     * @param tPayOrder 支付记录
     * @return 结果
     */
    @Override
    public int insertTPayOrder(PayOrder tPayOrder) {
        tPayOrder.setCreateTime(DateUtils.getNowDate());
        return baseMapper.insert(tPayOrder);
    }

    @Override
    public int batchInsert(List<PayOrder> list) {
        return baseMapper.insertBatch(list);
    }

    @Override
    public int batchUpdate(List<PayOrder> list) {
        return baseMapper.updateBatch(list);
    }

    @Override
    public PayOrderDto selectParkingOrderByToday(Integer province, Integer city, Integer district, Long deptId, Long parkInfoId) {
        return payOrderMapper.selectParkingOrderByToday(province, city, district, deptId, parkInfoId);
    }

    @Override
    public List<PayOrderDto> selectParkingOrderCountByMonth(Integer year, Integer startMonth, Integer endMonth, Long parkId, List<String> payStatusList) {
        PayOrderDto orderDto;
        List<PayOrderDto> parkOrderDtoList = new ArrayList<>();
        for (int month = startMonth; month <= endMonth; month++) {
            if (month <= 0) {
                int newMonth = month + 12;
                int newYear = year - 1;
                orderDto = payOrderMapper.selectParkingOrderCountByMonthAndStatus(newYear, newMonth, parkId, payStatusList);
                orderDto.setYearMonth(newYear + "-" + newMonth);
            } else {
                orderDto = payOrderMapper.selectParkingOrderCountByMonthAndStatus(year, month, parkId, payStatusList);
                orderDto.setYearMonth(year + "-" + month);
            }
            parkOrderDtoList.add(orderDto);
        }
        return parkOrderDtoList;
    }

    @Override
    public List<PayOrderDto> selectParkingOrderCountByMonth(Integer year, Integer startMonth, Integer endMonth, Integer province,
                                                            Integer city, Integer district, Long deptId, Long parkInfoId) {
        PayOrderDto orderDto;
        List<PayOrderDto> parkOrderDtoList = new ArrayList<>();
        for (int month = startMonth; month <= endMonth; month++) {
            if (month <= 0) {
                int newMonth = month + 12;
                int newYear = year - 1;
                orderDto = payOrderMapper.selectParkingOrderCountByMonth(newYear, newMonth, province, city, district, deptId, parkInfoId);
                orderDto.setMonth(newMonth);
            } else {
                orderDto = payOrderMapper.selectParkingOrderCountByMonth(year, month, province, city, district, deptId, parkInfoId);
                orderDto.setMonth(month);
            }
            parkOrderDtoList.add(orderDto);
        }
        return parkOrderDtoList;
    }

    @Override
    public List<PayOrderDto> selectParkingRevenueRanking(Integer limit, Integer province, Integer city,
                                                         Integer district, Long deptId, Long parkInfoId) {
        return payOrderMapper.selectParkingRevenueRanking(limit, province, city, district, deptId, parkInfoId);
    }

    @Override
    public BigDecimal selectTotalAnnualAmount(Integer province, Integer city, Integer district, Long deptId, Long parkInfoId) {
        return payOrderMapper.selectTotalAnnualAmount(province, city, district, deptId, parkInfoId);
    }

    @Override
    public PayOrderDto selectPaymentMethodOnThreeDay(String startTime, String endTime, Integer province, Integer city,
                                                     Integer district, Long deptId, Long parkInfoId) {
        return payOrderMapper.selectPaymentMethodOnThreeDay(startTime, endTime, province, city, district, deptId, parkInfoId);
    }

    @Override
    public PayOrderDto selectParkingDuration(String startTime, String endTime, Integer province, Integer city,
                                             Integer district, Long deptId, Long parkInfoId) {
        return payOrderMapper.selectParkingDuration(startTime, endTime, province, city, district, deptId, parkInfoId);
    }

    @Override
    public PayOrderDto selectPaymentMethodToday(Integer province, Integer city, Integer district, Long deptId, Long parkInfoId) {
        return payOrderMapper.selectPaymentMethodToday(province, city, district, deptId, parkInfoId);
    }

    @Override
    public PageResult selectPageList(PayOrderRequest payOrderRequest) {
        // 根据项目的支付渠道配置分逻辑查询
        Long parkId = payOrderRequest.getParkId();
        R<SystemProjectDTO> systemProjectR = remoteSystemProjectService.getProjectInfo(parkId);
        if (null != systemProjectR && null != systemProjectR.getData()) {
            SystemProjectDTO systemProject = systemProjectR.getData();
            String payChannel = systemProject.getPayChannel();
            if (StrUtil.equals(payChannel, PayChannelEnum.PP.getChannel())) {
                // PP
                payOrderRequest.setPayChannel(PayChannelEnum.PP.getChannel());
                payOrderRequest.setStatus(Arrays.asList(PayStatus.PAID.getStatus(), PayStatus.CASH_PAID.getStatus()));
                return selectPageListToPp(payOrderRequest);
            } else if (StrUtil.equals(payChannel, PayChannelEnum.ICBC.getChannel())) {
                // ICBC
                payOrderRequest.setPayChannel(PayChannelEnum.ICBC.getChannel());
                return selectPageListToIcbc(payOrderRequest);
            } else {
                log.warn("[支付记录]-分页查询支付记录，支付渠道未匹配,{},{}", parkId, systemProject);
                return PageResult.getEmptyInstance(payOrderRequest.getPageNum(), payOrderRequest.getPageSize());
            }
        } else {
            log.warn("[支付记录]-分页查询支付记录，车场信息获取失败,{}", parkId);
            return PageResult.getEmptyInstance(payOrderRequest.getPageNum(), payOrderRequest.getPageSize());
        }
    }

    private PageResult selectPageListToIcbc(PayOrderRequest payOrderRequest) {
        IPage<PayOrderVo> pageResult = doSelectPageList(payOrderRequest);
        List<PayOrderVo> payOrderVoList = pageResult.getRecords();

        if (CollUtil.isNotEmpty(payOrderVoList)) {
            //  订单笔数、订单总金额、支付总金额、总手续费、应结算总金额
            PayOrderVo payOrderVo = payOrderMapper.selectStatisticalInfoByPayType(payOrderRequest);

            // 判断支付是否有退款
            RefundOrder refundOrderSearch = new RefundOrder();
            refundOrderSearch.setParkId(payOrderRequest.getParkId());
            payOrderVoList.forEach(payOrder -> {
                String orderNo = payOrder.getOrderNo();
                if (StrUtil.isNotBlank(orderNo)){
                    refundOrderSearch.setOrderNo(orderNo);
                    List<RefundOrder> refundOrderList = refundOrderService.selectList(refundOrderSearch);
                    // 赋值操作人信息
                    if (CollUtil.isNotEmpty(refundOrderList)){
                        StringBuilder operateIds = new StringBuilder();
                        refundOrderList.forEach(refundOrder -> {
                            Long createUserId = refundOrder.getCreateUser();
                            if (null != createUserId) {
                                operateIds.append(createUserId).append(",");
                            }
                        });

                        if (StrUtil.isNotBlank(operateIds)) {
                            R<List<OperateDTO>> updateUserResult = remoteSystemUserService.getOperateList(operateIds.toString());
                            if (null != updateUserResult.getData()) {
                                List<OperateDTO> operateDTOList = updateUserResult.getData();
                                if (CollUtil.isNotEmpty(operateDTOList)) {
                                    Map<Long, String> operateMap = operateDTOList.stream().collect(Collectors.toMap(OperateDTO::getOperateId, OperateDTO::getOperateName));
                                    refundOrderList.forEach(refundOrder -> refundOrder.setUpdateBy(operateMap.get(refundOrder.getCreateUser())));
                                }
                            }
                        }
                    }

                    payOrder.setRefundOrderList(refundOrderList);
                }
            });

            return PageResult.builder()
                    .pageNum(payOrderRequest.getPageNum())
                    .pageSize(payOrderRequest.getPageSize())
                    .totalPages(pageResult.getPages())
                    .totalSize(pageResult.getTotal())
                    .num(payOrderVo.getTotalCount())
                    .totalPrice(payOrderVo.getTotalPrice() != null ? payOrderVo.getTotalPrice() : BigDecimal.ZERO)
                    .totalPayPrice(payOrderVo.getTotalPayPrice() != null ? payOrderVo.getTotalPayPrice() : BigDecimal.ZERO)
                    .totalServiceCharge(payOrderVo.getTotalServiceCharge() != null ? payOrderVo.getTotalServiceCharge() : BigDecimal.ZERO)
                    .totalSettlementAmount(payOrderVo.getTotalSettlementAmount() != null ? payOrderVo.getTotalSettlementAmount() : BigDecimal.ZERO)
                    .list(payOrderVoList)
                    .build();
        } else {
            return PageResult.getEmptyInstance(payOrderRequest.getPageNum(), payOrderRequest.getPageSize());
        }
    }

    private PageResult selectPageListToPp(PayOrderRequest payOrderRequest) {
        IPage<PayOrderVo> pageResult = doSelectPageList(payOrderRequest);
        List<PayOrderVo> payOrderVoList = pageResult.getRecords();

        if (CollUtil.isNotEmpty(payOrderVoList)) {
            StringBuilder operateIds = new StringBuilder();
            payOrderVoList.forEach(payOrderVo -> {
                if (PayType.ETC.getType().equals(payOrderVo.getPayType())
                        && (payOrderVo.getActualFee() == null || payOrderVo.getActualFee().compareTo(BigDecimal.ZERO) == 0)) {
                    payOrderVo.setActualFee(payOrderVo.getAmt());
                }
                Long updateUserId = payOrderVo.getUpdateUser();
                if (null != updateUserId) {
                    operateIds.append(updateUserId).append(",");
                }
            });

            // 赋值操作人信息
            if (StrUtil.isNotBlank(operateIds)) {
                R<List<OperateDTO>> updateUserResult = remoteSystemUserService.getOperateList(operateIds.toString());
                if (null != updateUserResult.getData()) {
                    List<OperateDTO> operateDTOList = updateUserResult.getData();
                    if (CollUtil.isNotEmpty(operateDTOList)) {
                        Map<Long, String> operateMap = operateDTOList.stream().collect(Collectors.toMap(OperateDTO::getOperateId, OperateDTO::getOperateName));
                        payOrderVoList.forEach(payOrderVo -> payOrderVo.setUpdateBy(operateMap.get(payOrderVo.getUpdateUser())));
                    }
                }
            }

            PayOrderVo payOrderVo = payOrderMapper.selectStatisticalInfoByPayType(payOrderRequest);

            long num = payOrderVo.getTotalCount();
            BigDecimal totalPrice = payOrderVo.getTotalPrice();

            return PageResult.builder()
                    .pageNum(payOrderRequest.getPageNum())
                    .pageSize(payOrderRequest.getPageSize())
                    .totalPages(pageResult.getPages())
                    .totalSize(pageResult.getTotal())
                    .list(payOrderVoList)
                    .num(num)
                    .totalPrice(totalPrice)
                    .build();
        }

        return PageResult.builder()
                .pageNum(payOrderRequest.getPageNum())
                .pageSize(payOrderRequest.getPageSize())
                .totalPages(0L)
                .totalSize(0L)
                .list(payOrderVoList)
                .num(0L)
                .totalPrice(BigDecimal.ZERO)
                .build();
    }

    private IPage<PayOrderVo> doSelectPageList(PayOrderRequest payOrderRequest) {
        Page<PayOrderVo> page = new Page<>();
        page.setCurrent(payOrderRequest.getPageNum());
        page.setSize(payOrderRequest.getPageSize());
        return payOrderMapper.selectPageList(page, payOrderRequest);
    }

    @Override
    public List<PayOrderDto> selectPayOrderByParkingSerial(Long parkId, String parkingSerial) {
        return payOrderMapper.selectPayOrderByParkingSerial(parkId, parkingSerial);
    }

    @Override
    public PayOrderVo selectStatisticsInfoByClientUserId(Long clientUserId, List<String> payTypeList, String beginTime, String endTime) {
        return payOrderMapper.selectStatisticsInfoByClientUserId(SecurityUtils.getParkId(), clientUserId, payTypeList, beginTime, endTime);
    }

    @Override
    public void updateCashSettlementFeeByClientUserId(Long clientUserId) {
        payOrderMapper.updateCashSettlementFeeByClientUserId(clientUserId);
    }

    /**
     * 查询支付记录列表
     *
     * @param payOrder 支付记录
     * @return 支付记录
     */
    @Override
    @NeedSetFieldValue
    @NeedSetDictLabel
    public List<PayOrder> selectPayOrderList(PayOrder payOrder) {
        return payOrderMapper.selectPayOrderList(payOrder);
    }

    @Override
    public Double selectElectronicStatistic(Long parkId) {
        return payOrderMapper.selectElectronicStatistic(parkId);
    }

    @Override
    public Double selectCashStatistic(Long parkId) {
        return payOrderMapper.selectCashStatistic(parkId);
    }

    @Override
    public Double selectWaitStatistic(Long parkId) {
        return payOrderMapper.selectWaitStatistic(parkId);
    }

    @Override
    public Double selectFreeStatistic(Long parkId) {
        return payOrderMapper.selectFreeStatistic(parkId);
    }

    @Override
    public Double selectCashStatisticByUserId(Long parkId, Long userId) {
        return payOrderMapper.selectCashStatisticByUserId(parkId, userId);
    }

    @Override
    public Double selectFreeStatisticByUserId(Long parkId, Long userId) {
        return payOrderMapper.selectFreeStatisticByUserId(parkId, userId);
    }

    @Override
    public PayOrderDto getRevenue(Long parkId, List<String> statusList, Date starDate, Date endDate) {
        return payOrderMapper.getRevenue(parkId, statusList, starDate, endDate);
    }

    @Override
    public BigDecimal selectNoPayFee(Long parkId, Integer userId, String beginTime, String endTime) {
        return payOrderMapper.selectNoPayFee(parkId, userId, beginTime, endTime);
    }

    @Override
    public PageResult selectPageListByPayType(Long clientUserId, String plate, String payType,
                                              Integer pageNum, Integer pageSize,
                                              String beginTime, String endTime) {
        Page<PayOrderVo> page = new Page<>();
        page.setCurrent(pageNum);
        page.setSize(pageSize);
        IPage<PayOrderVo> pageResult = payOrderMapper.selectPageListByPayType(page, clientUserId, plate, payType, beginTime, endTime);
        return PageResult.builder()
                .pageNum(pageNum)
                .pageSize(pageSize)
                .totalPages(pageResult.getPages())
                .totalSize(pageResult.getTotal())
                .list(pageResult.getRecords())
                .build();
    }

    @Override
    public BigDecimal selectFreeFee(Long parkId, Integer userId, String beginTime, String endTime) {
        return payOrderMapper.selectFreeFee(parkId, userId, beginTime, endTime);
    }

    @Override
    public PageResult selectPageNoChargeList(Long clientUserId, String plate,
                                             Integer pageNum, Integer pageSize,
                                             String beginTime, String endTime) {
        Page<PayOrderVo> page = new Page<>();
        page.setCurrent(pageNum);
        page.setSize(pageSize);
        IPage<PayOrderVo> pageResult = payOrderMapper.selectPageNoChargeList(page, clientUserId, plate, beginTime, endTime);
        return PageResult.builder()
                .pageNum(pageNum)
                .pageSize(pageSize)
                .totalPages(pageResult.getPages())
                .totalSize(pageResult.getTotal())
                .list(pageResult.getRecords())
                .build();
    }


    @Override
    public List<ParkStatisticsIncome> selectStatisticsIncomeByDay(Long parkId) {
        return payOrderMapper.selectStatisticsIncomeByDay(parkId);
    }

    @Override
    public ParkStatisticsIncome selectStatisticsIncomeByAppointDay(String day, Long parkId) {
        return payOrderMapper.selectStatisticsIncomeByAppointDay(day, parkId);
    }

    @Override
    public List<PayOrderDto> selectPayTypeCountByMonth(Integer year, Integer startMonth, Integer endMonth, Long parkId) {
        PayOrderDto orderDto;
        List<PayOrderDto> parkOrderDtoList = new ArrayList<>();
        for (int month = startMonth; month <= endMonth; month++) {
            if (month <= 0) {
                int newMonth = month + 12;
                int newYear = year - 1;
                orderDto = payOrderMapper.selectPayTypeCountByMonth(newYear, newMonth, parkId);
                orderDto.setYearMonth(newYear + "-" + newMonth);
            } else {
                orderDto = payOrderMapper.selectPayTypeCountByMonth(year, month, parkId);
                orderDto.setYearMonth(year + "-" + month);
            }
            parkOrderDtoList.add(orderDto);
        }
        return parkOrderDtoList;
    }

}
